package com.sunxd.zookeeper.use.common;

import com.alibaba.fastjson.JSON;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author: 作者名称
 * @date: 2021-06-07 19:35
 **/
@Component
@RequiredArgsConstructor
@Slf4j
public class ZkUtil {

    private final ZkClientConfig zkClientConfig;

    private final static String NULL = null;

    /**
     * 创建永久节点
     * @param path 路径
     * @param data 数据
     * @return 返回值，成功 路径path,失败 @EMPTY_STRING
     */
    public String createP(@NonNull String path, String data){
        try {
            return zkClientConfig.getZkClient().create(path,data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        } catch (Exception e) {
            log.error(" 节点创建失败 path:{},data:{}",path,data);
        }
        return NULL;
    }


    /**
     * 创建临时节点
     * @param path 路径
     * @param data 数据
     * @return 返回值，成功 路径path,失败 @EMPTY_STRING
     */
    public String createE(@NonNull String path, String data){
        try {
            return zkClientConfig.getZkClient().create(path,data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
        } catch (Exception e) {
            log.error(" 节点创建失败 path:{},data:{}",path,data);
        }
        return NULL;
    }

    /**
     * 修改节点
     * @param path 路径
     * @param data 数据
     * @return 返回值，成功 路径path,失败 @null
     */
    public Stat setData(@NonNull String path, String data){
        try {
            return zkClientConfig.getZkClient().setData(path,data.getBytes(),-1);
        } catch (Exception e) {
            log.error(" 节点修改失败 path:{},data:{}",path,data);
        }
        return null;
    }

    /**
     * 修改节点
     * @param path 路径
     * @param data 数据
     * @return 返回值，成功 路径path,失败 @null
     * 如果版本和传入的版本不一致，也会失败
     */
    public Stat setWithVersion(@NonNull String path, String data,@NonNull int version){
        try {
            return zkClientConfig.getZkClient().setData(path,data.getBytes(),version);
        } catch (Exception e) {
            log.error(" 节点修改失败 path:{},data:{},version:{}",path,data,version);
        }
        return null;
    }

    /**
     * 删除节点
     * @param path 路径
     * 如果版本和传入的版本不一致，也会失败
     */
    public void delete(@NonNull String path){
        try {
            zkClientConfig.getZkClient().delete(path,-1);
        } catch (Exception e) {
            log.error(" 节点删除失败 path:{}",path);
        }
    }

    /**
     * 删除节点
     * @param path 路径
     * 如果版本和传入的版本不一致，也会失败
     */
    public void deleteWithVersion(@NonNull String path,@NonNull int version){
        try {
            zkClientConfig.getZkClient().delete(path,version);
        } catch (Exception e) {
            log.error(" 节点删除失败 path:{},version:{}",path,version);
        }
    }

    /**
     * 判断节点是否存在
     * @param path 路径
     */
    public Stat exists(@NonNull String path,boolean watch){
        try {
            return zkClientConfig.getZkClient().exists(path,watch);
        } catch (Exception e) {
            log.error(" 判断节点失败 path:{},watch:{}",path,watch);
        }
        return null;
    }

    /**
     * 判断节点是否存在，并设置监听
     * @param path 路径
     */
    public Stat exists(@NonNull String path, Watcher watch){
        try {
            return zkClientConfig.getZkClient().exists(path,watch);
        } catch (Exception e) {
            log.error(" 判断节点失败 path:{},watch:{}",path,watch);
        }
        return null;
    }

    /**
     * 获取子节点-不包含 孙子节点
     * @param path 路径
     */
    public List<String> getChildren(@NonNull String path, Watcher watch){
        try {
            return zkClientConfig.getZkClient().getChildren(path,false);
        } catch (Exception e) {
            log.error(" 获取子节点 error path:{},watch:{}",path,watch);
        }
        return null;
    }

    /**
     * 获取指定节点的值 是否启用监听
     * @param path 路径
     * @return ZkData
     */
    public ZkData getWatchData(@NonNull String path, boolean flag){
        try {
            Stat stat = new Stat();
            String data = new String(zkClientConfig.getZkClient().getData(path,flag,stat));
            return ZkData.builder().stat(stat).data(data).build();
        } catch (Exception e) {
           log.error("获取数据失败 :path:{}",path);
        }
        return null;
    }

    /**
     * 获取指定节点的所有值
     * @param path 路径
     * @param watcher 监听器
     * @return String
     */
    public ZkData getData(@NonNull String path, Watcher watcher){
        try {
            Stat stat = new Stat();
            String data = new String(zkClientConfig.getZkClient().getData(path,watcher,stat));
            return ZkData.builder().stat(stat).data(data).build();
        } catch (Exception e) {
           log.error("获取数据失败 :path:{},watch:{}",path,watcher);
        }
        return null;
    }

    /**
     * 只获取指定节点的值
     * @param path 路径
     * @param watcher 监听器
     * @return String
     */
    public String getStringData(@NonNull String path,Watcher watcher){
        try {
            return new String(zkClientConfig.getZkClient().getData(path,watcher,new Stat()));
        } catch (Exception e) {
           log.error("获取数据失败 :path:{},watch:{}",path,watcher);
        }
        return NULL;
    }

    /**
     * 获取指定节点的stat
     * @param path 路径
     * @param watcher 监听器
     * @return String
     */
    public String getStat(@NonNull String path,Watcher watcher){
        try {
            Stat stat = new Stat();
            return new String(zkClientConfig.getZkClient().getData(path,watcher,stat));
        } catch (Exception e) {
           log.error("获取数据失败 :path:{},watch:{}",path,watcher);
        }
        return NULL;
    }


    /**
     * 获取指定节点的所有值
     * @param path 路径
     * @return String
     */
    public ZkData getDataCallback(@NonNull String path, AsyncCallback.DataCallback callback,Object ctx){
        try {
            zkClientConfig.getZkClient().getData(path,false, callback,ctx);
        } catch (Exception e) {
            log.error("获取数据失败 :path:{},ctx:{}",path, JSON.toJSONString(ctx));
        }
        return null;
    }

}
